package cn.zjutleo.village_server.service;

import cn.zjutleo.village_server.exception.apiException.daoException.SelectException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.*;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;

/**
 * @author : Qin Zhenghan
 * @date : Created in 2021/8/23
 * @description: Elasticsearch服务类
 */
@Slf4j
@Service
public class ElasticSearchService {

    @Resource
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    /**
     * 创建索引(无须判断索引是否已存在)
     *
     * @param clazz 创建索引的实体类类型
     * @return 是否创建成功
     */
    public boolean createIndex(Class<?> clazz) {
        return elasticsearchRestTemplate.createIndex(clazz);
    }

    /**
     * 删除索引
     *
     * @param clazz 创建索引的实体类类型
     * @return 是否删除成功
     */
    public boolean deleteIndex(Class<?> clazz) {
        return elasticsearchRestTemplate.deleteIndex(clazz);
    }

    /**
     * 添加文档
     *
     * @param id 数据对象id
     * @param object 数据对象
     * @param indexName 索引名称
     * @return 文档id
     */
    public String saveDoc(String id, Object object, String indexName) {
        IndexQuery indexQuery = new IndexQuery();
        indexQuery.setId(id);
        indexQuery.setObject(object);
        IndexCoordinates indexCoordinates = IndexCoordinates.of(indexName);
        return elasticsearchRestTemplate.index(indexQuery, indexCoordinates);
    }


    /**
     * 批量添加文档
     *
     * @param indexQueries 索引查询列表
     * @param indexName 索引名称
     */
    public void saveBatchDocs(List<IndexQuery> indexQueries, String indexName) {
        IndexCoordinates indexCoordinates = IndexCoordinates.of(indexName);
        elasticsearchRestTemplate.bulkIndex(indexQueries, indexCoordinates);
    }

    /**
     * 删除文档
     *
     * @param clazz 实体类
     * @param id 数据对象id
     */
    public String deleteDoc(String id, Class<?> clazz) {
        return elasticsearchRestTemplate.delete(id, clazz);
    }

    /**
     * 修改文档数据(实现部分修改)
     *
     * @param id 数据对象id
     * @param indexName 索引名称
     * @param fields 修改字段
     * @return UpdateResponse
     */
    public UpdateResponse updateDoc(String id, String indexName, Map<String, Object> fields) {
        Document document = Document.create();
        fields.forEach(document::put);
        UpdateQuery updateQuery = UpdateQuery
                .builder(id)
                .withDocument(document)
                .build();
        IndexCoordinates indexCoordinates = IndexCoordinates.of(indexName);
        return elasticsearchRestTemplate.update(updateQuery, indexCoordinates);
    }

    /**
     * 查询文档(支持匹配多字段、排序、分页)
     *
     * @param indexName 索引名称
     * @param queryValue 需要查询的值
     * @param sortBy 排序依据
     * @param order 排序方式(降序、升序)
     * @param pageNum 页码
     * @param pageSize 页大小
     * @param clazz 实体类型
     * @param fields 查询的字段(支持数组)
     * @param <T> 类型
     * @return 查询类列表
     * @throws SelectException 字段类型错误
     */
    public <T> List<T> queryDocs(String indexName,
                                 String queryValue,
                                 String sortBy,
                                 String order,
                                 int pageNum,
                                 int pageSize,
                                 Class<T> clazz,
                                 String... fields) throws SelectException {
        SortOrder sortOrder = SortOrder.DESC;
        if (!StringUtils.isEmpty(order)) {
            switch (order) {
                case "asc": {
                    sortOrder = SortOrder.ASC;
                    break;
                }
                case "desc": {
                    sortOrder = SortOrder.DESC;
                    break;
                }
                default: {
                    throw new SelectException("'order'字段值错误");
                }
            }
        }
        NativeSearchQuery nativeSearchQuery = new NativeSearchQueryBuilder()
                .withQuery(QueryBuilders.multiMatchQuery(queryValue, fields))
                .withSort(SortBuilders.fieldSort(sortBy).order(sortOrder))
                .withPageable(PageRequest.of(pageNum, pageSize))
                .build();
        IndexCoordinates indexCoordinates = IndexCoordinates.of(indexName);
        return elasticsearchRestTemplate.queryForList(nativeSearchQuery, clazz, indexCoordinates);
    }

}